package org.apache.lucene.ocean.database;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.apache.lucene.distributed.RMI;
import org.apache.lucene.ocean.database.OceanDatabase.Action;
import org.apache.lucene.ocean.database.OceanDatabase.ActionResult;

/**
 * Commits updates to 1 or more nodes in parallel
 * 
 */
// TODO: figure out how to rollback
public class OceanCellClient {
  private InetSocketAddress[] hosts;
  private RMI rmi;
  private ExecutorService threadPool;

  public OceanCellClient(InetSocketAddress[] hosts, RMI rmi, ExecutorService threadPool) {
    this.hosts = hosts;
    this.rmi = rmi;
    this.threadPool = threadPool;
  }

  // TODO: throw an error if none of the updates worked
  /**
   * Performs updates to the nodes.  If at least one update worked the 
   * transaction is considered to be successful.  It may be good
   * to report failed nodes to the name service
   */
  public ActionResult perform(Action action) throws Exception {
    List<Future<ActionResult>> futures = new ArrayList<Future<ActionResult>>(hosts.length);
    for (InetSocketAddress host : hosts) {
      Future<ActionResult> future = threadPool.submit(new Update(host, action));
      futures.add(future);
    }
    int errors = 0;
    for (Future<ActionResult> future : futures) {
      try {
        ActionResult result = future.get();
      } catch (ExecutionException ee) {
        errors++;
      }
    }
    if (errors == hosts.length) {
      throw new Exception("none of the actions worked");
    }
    return new ActionResult(action.actionId);
  }

  public class Update implements Callable<ActionResult> {
    Action action;
    InetSocketAddress host;
    OceanDatabase oceanDatabase;

    public Update(InetSocketAddress host, Action action) throws IOException {
      this.host = host;
      this.action = action;
      oceanDatabase = (OceanDatabase) rmi.getProxy("oceanDatabase", OceanDatabase.class, 1l, host, false);
    }

    public ActionResult call() throws Exception {
      return oceanDatabase.perform(action);
    }
  }
}
